package net;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


/**
 * 聊天(Chat)室服务端
 */
public class MapThreadChatServer {//ThreadChatServer.class
    //定义服务端的ServerSocket属性
    private ServerSocket serverSocket;
    //构建一个集合对象，存储所有客户端的输出流对象
   //private List<PrintWriter> allOut=new ArrayList<>();
    //构建一个集合对象，存储所有用户昵称和客户端的输出流对象
    private Map<String,PrintWriter> allOut=new HashMap<>();
    private static final int PORT;
    private ExecutorService executorService;
    static {
        PORT = 8088;//可以从配置文件读取
    }
    public MapThreadChatServer()  {
        try {
            System.out.println("服务端开始启动");
            serverSocket = new ServerSocket(PORT);
            executorService= Executors.newFixedThreadPool(10);
            System.out.println("服务器启动OK,正在"+PORT+"端口上进行监听");
        }catch (Exception e){
            e.printStackTrace();//让运维人员知道哪里出错了(后续这些信息记录到日志文件)
            System.out.println("服务器启动失败");
            System.exit(-1);
        }
    }
    /**
     * 线程任务类型
     */
    private class ClientHandler implements Runnable{
        private Socket socket;
        private String host;
        private String nickName;

        public ClientHandler(Socket socket){
            this.socket = socket;
            //获取客户端地址信息
            host=this.socket.getInetAddress().getHostName();

        }
        @Override
        public void run() {
            PrintWriter pw=null;

            try {
                //1.构建输入流对象(字符缓冲输入流)
                BufferedReader br = new BufferedReader(
                        new InputStreamReader(
                                socket.getInputStream(), StandardCharsets.UTF_8));
                //2.构建输出流对象(字符缓冲输出流)
               pw = new PrintWriter(
                        new BufferedWriter(
                                new OutputStreamWriter(socket.getOutputStream(),
                                        StandardCharsets.UTF_8)),true);
                //读取客户端上线的昵称
                nickName=br.readLine();
                synchronized (allOut) {
                    //将每个线程的输出流对象加入到集合中
                    allOut.put(nickName,pw);
                }
                //发消息给所有客户端，告知谁上线了
                sendMessageToAllClient(nickName+"上线了");
                //3.通过此对象读取写数据
                String data = null;
                while ((data = br.readLine()) != null) {
                    System.out.println(host +" say:" + data);
                    if(data.startsWith("@")){//这里以@开头表示给某个人发消息
                       //给某个人发消息
                       sendMessageToOneClient(data);
                    }else {
                      //发送消息给所有客户端
                       sendMessageToAllClient(data);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }finally{
                try {
                    synchronized (allOut) {
                        allOut.remove(nickName);
                    }
                    //关闭资源
                    socket.close();
                    System.out.println("客户端退出");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        //发消息给指定用户,消息格式: @nickName:msg
        private void sendMessageToOneClient(String data){
            //获取msg内容
            String msg=data.substring(data.lastIndexOf(":")+1);
            //获取nickName
            String toNickName=data.substring(1,data.lastIndexOf(":"));
            //给nickName发消息
            if(allOut.containsKey(toNickName)) {
                allOut.get(toNickName).println(host + " say:" + msg);
            }else{
                sendMessageToAllClient(toNickName+"用户已经下线");
            }
        }
        //发消息给所有用户
        private void sendMessageToAllClient(String data){
            System.out.println("client msg:"+data);
            //写数据给所有客户端)
            synchronized (allOut) {
                //迭代所有的输出流
                Collection<PrintWriter> values = allOut.values();
                for (PrintWriter out : values) {
                    out.println(host + " say:" + data);
                }
            }
        }
    }
    //启动服务读取数据
    public void start(){
        while(true) {
            try {
                //1.接收客户端的连接(主线程-接线员)
                Socket socket = serverSocket.accept();
                System.out.println("客户端连接成功");
                //2.启动线程处理客户端请求(读取请求中数据)
                //new Thread(new ClientHandler(socket)).start();
                executorService.execute(new ClientHandler(socket));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        new MapThreadChatServer().start();
    }

}
